#include "common.h"
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/socket.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>

#include <stdio.h>

void *read_from_server(void *);
void *write_to_server(void *);

int main(void){
  int res=0; // 保存处理结果
  // 保存创建成功的客户端fd,和连接成功的服务器端fd
  int server_socket_fd=0,client_socket_fd=0;
  // 定义服务器端和客户端套接字结构体变量
  struct sockaddr_in server_socket_addr, client_socket_addr;
  // 初始化
  memset(&server_socket_addr,0,sizeof(server_socket_addr));
  memset(&client_socket_addr,0,sizeof(client_socket_addr));
  // 设置当前客户端地址信息
  client_socket_addr.sin_family=AF_INET;//IPV4协议
  // 客户端地址配置成当前本机
  inet_pton(AF_INET,"192.168.1.3",&client_socket_addr.sin_addr.s_addr);
  // 设置客户端端口号,todo:使用随机端口号，否则客户端程序停止后，要等会才能启动
  client_socket_addr.sin_port=htons(8083);

  // 设置当前客户端要连接的服务器地址信息
  server_socket_addr.sin_family=AF_INET;//IPV4协议
  // 客户端地址配置成当前本机
  inet_pton(AF_INET,"192.168.1.3",&server_socket_addr.sin_addr.s_addr);
  // 设置服务器端端口号
  server_socket_addr.sin_port=htons(8080);

  // 通信操作流程
  // 1.创建客户端 socket
  client_socket_fd = socket(AF_INET, SOCK_STREAM, 0);
  handle_error("socket",client_socket_fd);//异常处理
  // 2.绑定客户端地址
  res=bind(client_socket_fd,(struct sockaddr *)&client_socket_addr,sizeof(client_socket_addr));
  handle_error("bind",res);//异常处理  
  // 3.客户端主动跟服务器进行连接
  socklen_t len = sizeof(server_socket_addr);
  res=connect(client_socket_fd,(struct sockaddr *)&server_socket_addr,len);
  handle_error("connect",res);//异常处理
  // 4.进行通信业务操作
  printf("客户端:与服务器%s:%d成功建立连接,通信描述符为%d\n",
        inet_ntoa(server_socket_addr.sin_addr),
        ntohs(server_socket_addr.sin_port),
        client_socket_fd);
  // 创建子线程，用于收发消息
  pthread_t t_read,t_write;
  pthread_create(&t_read,NULL,read_from_server,&client_socket_fd);
  pthread_create(&t_write,NULL,write_to_server,&client_socket_fd);
  
  // 阻塞主线程，让子线程一直执行，直到其内部自动结束
  pthread_join(t_read,NULL);
  pthread_join(t_write,NULL);

  // 通信结束后，释放资源
  printf("客户端:通信结束，连接关闭，释放资源。。。。");
  close(client_socket_fd);

  return 0;
}

/**
 * @brief  读取来自服器的消息-子线程执行函数
 * @param  *sockfd 与服器通信的sockfd指针
 * @return 无
 */
void *read_from_server(void *sockfd){
  // 解引用获取fd
  int client_fd=*((int *)sockfd);
  char *buffer=malloc(sizeof(char)*1024);
  if(buffer==NULL){
    perror("malloc");
    return NULL;
  }
  // 一直准备接收数据
  ssize_t size;
  while((size=recv(client_fd,buffer,1024,0))){ 
    if(size<0){ // 本次收到的消息有异常
      perror("recv");
    }
    fputs(buffer,stdout);
  }
  // size为0，说明服器不再给客户端发送消息
  printf("客户端:服器请求断开连接\n");
  free(buffer);
  return NULL;
}
/**
 * @brief  发送消息给服务器-子线程执行函数
 * @param  *sockfd 与服器通信的sockfd指针
 * @return 无
 */
void *write_to_server(void *sockfd){
  // 解引用获取fd
  int client_fd=*((int *)sockfd);
  char *buffer=malloc(sizeof(char)*1024);
  if(buffer==NULL){
    perror("malloc");
    return NULL;
  }
  // 若控制台按下ctrl+D，会收到null 
  ssize_t size = 0;
  while(fgets(buffer,1024,stdin)!=NULL){
    size = send(client_fd,buffer,1024,0);
    if(size<0){
      perror("send");
    }
  }
  // 服务器端不想跟服器通信了，要结束
  printf("收到来自控制台的结束发送请求，不再向服器发送数据，向服器发起关闭连接的请求...\n");
  shutdown(client_fd,SHUT_WR);
  free(buffer);
  return NULL;
}